using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Windows;

namespace ReactiveCode
{
    using Collections;
    using Expressions;
    using Linq;
    using Util;

    public class ReactiveHost: IDisposable
    {
        #region Scheduler

        StateChangeScheduler _scheduler = StateChangeScheduler.Current;
        internal StateChangeScheduler Scheduler
        {
            get { return _scheduler; }
        }
        
        #endregion

        Time _creationTime = Time.Current;
        internal Time CreationTime
        {
            get { return _creationTime; }
        }

        ~ReactiveHost()
        {
            Dispose(false);
        }
        public void Dispose()
        {
            Dispose(true);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                GC.SuppressFinalize(this);
            foreach(var call in _handles.ToList())
                call.Dispose();
        }

        public void DoSync()
        {
            Scheduler.DoSync();
        }

        ObservableLinkedList<ReactiveHandle> _handles = new ObservableLinkedList<ReactiveHandle>();
        IObservableEnumerable<ReactiveHandle> _readOnlyHandles;
        public IObservableEnumerable<ReactiveHandle> Handles
        {
            get { return _readOnlyHandles ?? (_readOnlyHandles = _handles.AsReadOnly()); }
        }

        internal IDisposable AddHandle(ReactiveHandle handle)
        {
            Contract.Requires<ArgumentNullException>(handle != null, "handle");
            Contract.Ensures(Contract.Result<IDisposable>() != null);
            var node = _handles.AddLast(handle);
            return Disposable.Create(() => _handles.Remove(node));
        }

        public ReactiveHandle<T> Call<T>(ReactiveMethodDefinition<T> method, bool enable = true)
        {
            Contract.Requires<ArgumentNullException>(method != null, "method");
            Contract.Ensures(Contract.Result<ReactiveHandle>() != null);
            Contract.Ensures(Contract.Result<ReactiveHandle>().Enabled == enable);
            var handle = new ReactiveHandle<T>(this, method);
            if (enable)
                handle.Enabled = true;
            return handle;
        }
        public ReactiveHandle<T> Call<T>(ReactiveExpression expression, bool enable = true)
        {
            Contract.Requires<ArgumentNullException>(expression != null, "expression");
            Contract.Ensures(Contract.Result<ReactiveHandle>() != null);
            Contract.Ensures(Contract.Result<ReactiveHandle>().Enabled == enable);
            return Call(new ReactiveMethodDefinition<T>(() => expression), enable);
        }
        public ReactiveHandle<Unit> Call(ReactiveExpression expression, bool enable = true)
        {
            return Call<Unit>(expression, enable);
        }

        public event EventHandler<ReactiveUnhandledExceptionEventArgs> UnhandledException;
        internal void OnError(ReactiveUnhandledExceptionEventArgs e)
        {
            var handler = UnhandledException;
            if (handler != null)
                handler(this, e);
        }
    }
}